﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using NNote.DocumentModel;
using NNote.Properties;
using System.Text.RegularExpressions;
using Liquid.Windows;

namespace NNote {
	public partial class PlainTextEditor : RichTextBox, IDocumentViewer {
		public PlainTextEditor(Document document) {
			this.Document = document;
			this.Dock = DockStyle.Fill;
			this.DetectUrls = false;
			this.AcceptsTab = true;
			this.ShowSelectionMargin = false;
			this.ScrollBars = RichTextBoxScrollBars.ForcedBoth;
			this.WordWrap = false;
			if (document.Filename != null) {
				using (var reader = new StreamReader(document.Filename, true)) {
					Text = reader.ReadToEnd();
				}
			}
			Modified = false;
			this.ModifiedChanged += new EventHandler(PlainTextEditor_ModifiedChanged);
			this.TextChanged += new EventHandler(PlainTextEditor_TextChanged);
			((IDocumentViewer)this).OnSettingsChanged();
		}

		bool m_LastCanUndo;
		bool m_LastCanRedo;
		string m_LastUndoActionName;
		string m_LastRedoActionName;
		void PlainTextEditor_TextChanged(object sender, EventArgs e) {
			if (m_LastCanUndo != CanUndo || m_LastUndoActionName != UndoActionName) {
				OnUndoChanged(EventArgs.Empty);
				m_LastCanUndo = CanUndo;
				m_LastUndoActionName = UndoActionName;
			}
			if (m_LastCanRedo != CanRedo || m_LastRedoActionName != RedoActionName) {
				OnRedoChanged(EventArgs.Empty);
				m_LastCanRedo = CanUndo;
				m_LastRedoActionName = RedoActionName;
			}
		}

		void PlainTextEditor_ModifiedChanged(object sender, EventArgs e) {
			if (this.Modified) {
				Document.Modified = true;
			}
		}

		void IDocumentViewer.OnSettingsChanged() {
			using (this.SuspendDrawing()) {
				this.BackColor = Settings.Default.BackColor
					?? SystemColors.Window;
				this.ForeColor = Settings.Default.TextColor
					?? SystemColors.WindowText;
				this.Font = Settings.Default.TextFont
					?? SystemFonts.DefaultFont;
				this.WordWrap = Settings.Default.WordWrap;
			}
		}

		protected override bool ProcessCmdKey(ref Message m, Keys keyData) {
			switch (keyData) {
				case Keys.Tab:
				case Keys.Tab | Keys.Shift:
					if (this.SelectionLength == 0)
						break;
					if (!Regex.IsMatch(SelectedText, @"\S"))
						break;
					if ((keyData & Keys.Shift) == Keys.None) {
						DoBlockIndent();
					} else {
						DoBlockUnindent();
					}
					return true;
				case Keys.Control | Keys.V:
					this.Paste();
					return true;
				case Keys.Control | Keys.Subtract:
				case Keys.Control | Keys.OemMinus:
					ZoomFactor = Math.Max(0.015625f, ZoomFactor / 1.1f);
					return true;
				case Keys.Control | Keys.Add:
				case Keys.Control | Keys.Oemplus:
					ZoomFactor = Math.Min(63, ZoomFactor * 1.1f);
					return true;
			}
			return base.ProcessCmdKey(ref m, keyData);
		}

		private void DoBlockIndent() {
			using (this.SuspendDrawing()) {
				var selStart = this.SelectionStart;
				var selLength = this.SelectionLength;
				var selEnd = selStart + selLength;

				var startLine = GetLineFromCharIndex(selStart);
				var endLine = GetLineFromCharIndex(selEnd);
				if (selEnd == GetFirstCharIndexFromLine(endLine))
					endLine--;

				for (int line = endLine; line >= startLine; line--) {
					var index = GetFirstCharIndexFromLine(line);
					Select(index, 0);
					this.SelectedText = "\t";
					selLength++;
				}

				Select(selStart + 1, selLength - 1);
			}
		}
		private void DoBlockUnindent() {
			using (this.SuspendDrawing()) {
				var selStart = this.SelectionStart;
				var selLength = this.SelectionLength;
				var selEnd = selStart + selLength;

				var startLine = GetLineFromCharIndex(selStart);
				var endLine = GetLineFromCharIndex(selEnd);
				if (selEnd == GetFirstCharIndexFromLine(endLine))
					endLine--;

				for (int line = endLine; line >= startLine; line--) {
					var index = GetFirstCharIndexFromLine(line);
					Select(index, 1);
					if (this.SelectedText == "\t") {
						if (line == startLine) {
							selStart--;
							selLength++;
						}
						this.SelectedText = "";
						selLength--;
					}
				}
				var startLineIndex = GetFirstCharIndexFromLine(startLine);
				if (selStart <= startLineIndex) {
					selStart++;
					selLength--;
				}
				Select(selStart, selLength);
			}
		}

		public Document Document { get; private set; }

		Control IDocumentViewer.Control { get { return this; } }

		public int LineCount {
			get { throw new NotImplementedException(); }
		}

		public void GotoLine(int line) {
			this.Select(GetFirstCharIndexFromLine(line), 0);
		}

		public void Save(StreamWriter stream) {
			stream.Write(Text);
		}

		public string[] GetSupportedClipboardFormats() {
			return new string[] {
				DataFormats.UnicodeText,
				DataFormats.Text,
				DataFormats.StringFormat
			};
		}

		public event EventHandler UndoChanged;
		protected void OnUndoChanged(EventArgs e) {
			var handler = UndoChanged;
			if (handler != null)
				handler(this, e);
		}

		public event EventHandler RedoChanged;
		protected void OnRedoChanged(EventArgs e) {
			var handler = RedoChanged;
			if (handler != null)
				handler(this, e);
		}

		public new void Paste() {
			base.Paste(DataFormats.GetFormat(DataFormats.UnicodeText));
		}
	}
}
